﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Tw.Trains.Domain
{
    /// <summary> 车站线路图 </summary>
    public class TrainGraph
    {
        private readonly List<StationNode> _graphs;
        private const string TrainsRegex = "(?<start>[a-z])(?<end>[a-z])(?<distance>\\d+)";

        public TrainGraph()
        {
            _graphs = new List<StationNode>();
        }

        /// <summary> 起始站点数 </summary>
        public int NodeCount()
        {
            return _graphs.Count;
        }

        private int ToInt(string str, int def = 0)
        {
            int value;
            if (int.TryParse(str, out value))
                return value;
            return def;
        }

        private bool ExistStation(string name)
        {
            return !string.IsNullOrWhiteSpace(name) && _graphs.Exists(t => t.Name == name);
        }

        private bool ExistStation(Station station)
        {
            return station != null && ExistStation(station.Name);
        }

        private void Trip(List<TripNode> trips, TripNode parentTrip, Station start, Station end, TripCondition condition = null)
        {
            if (!ExistStation(start))
                return;
            var nextNode = _graphs.First(t => t == start);
            foreach (var item in nextNode.Nexts)
            {
                var trip = parentTrip.Clone();
                trip.Append(new TripNode(item.Key.Name) { Attribute = item.Value });
                var tripAttr = trip.TripAttribute();
                if ((condition == null && tripAttr.Stops > _graphs.Count) || (condition != null && !condition.Verify(tripAttr)))
                    continue;
                if (item.Key == end)
                {
                    trips.Add(trip.Clone());
                    if (condition == null)
                        continue;
                }
                Trip(trips, trip, item.Key, end, condition);
            }
        }

        /// <summary> 站点间距离 </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns>
        /// -1:no such route
        /// >0:distance
        /// </returns>
        public int Distance(Station start, Station end)
        {
            if (!ExistStation(start.Name))
                return -1;
            var graph = _graphs.First(t => t.Name == start.Name);
            if (graph.Nexts.ContainsKey(end))
                return graph.Nexts[end].Distance;
            return -1;
        }

        /// <summary> 线路方案 </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="condition"></param>
        /// <returns></returns>
        public List<TripNode> Trips(Station start, Station end, TripCondition condition)
        {
            if (condition == null || !ExistStation(start))
                return null;
            var trips = new List<TripNode>();
            var node = _graphs.First(t => t == start);
            foreach (var next in node.Nexts)
            {
                var trip = new TripNode(start.Name);
                trip.Append(new TripNode(next.Key.Name) { Attribute = next.Value });
                if (next.Key == end)
                {
                    var tripAttr = trip.TripAttribute();
                    if (condition.Verify(tripAttr))
                        trips.Add(trip);
                }
                Trip(trips, trip, next.Key, end, condition);
            }
            return trips;
        }

        /// <summary> 最短距离线路 </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        public TripNode BestTrip(Station start, Station end)
        {
            if (!ExistStation(start))
                return null;
            var trips = new List<TripNode>();
            var node = _graphs.First(t => t == start);
            foreach (var next in node.Nexts)
            {
                var trip = new TripNode(start.Name);
                trip.Append(new TripNode(next.Key.Name) { Attribute = next.Value });
                if (next.Key == end)
                {
                    trips.Add(trip);
                }
                Trip(trips, trip, next.Key, end);
            }
            return !trips.Any() ? null : trips.OrderBy(t => t.TripAttribute().Distance).First();
        }

        /// <summary> 树形站点数 </summary>
        public int GraphCount()
        {
            return _graphs.Sum(t => t.Nexts.Count);
        }

        /// <summary> 添加单个路线图 </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="distance"></param>
        public void AddTrain(Station start, Station end, int distance)
        {
            if (start == null || end == null || distance <= 0)
            {
                return;
            }
            StationNode node;
            if (!_graphs.Exists(t => t == start))
            {
                node = new StationNode(start.Name);
                _graphs.Add(node);
            }
            else
            {
                node = _graphs.First(t => t == start);
            }
            if (node.Nexts.ContainsKey(end))
            {
                TripAttribute attr;
                if (node.Nexts.TryGetValue(end, out attr))
                {
                    attr.Distance = distance;
                }
            }
            else
            {
                node.Nexts.TryAdd(end, new TripAttribute(distance) { Stops = 0 });
            }
        }

        /// <summary> 批量添加路线 </summary>
        /// <param name="graphs"></param>
        public void AddTrains(string graphs)
        {
            if (string.IsNullOrWhiteSpace(graphs))
                return;
            var nodes = graphs.Split(new[] { ",", "，", ";", "；" }, StringSplitOptions.RemoveEmptyEntries);
            if (!nodes.Any())
                return;
            var reg = new Regex(TrainsRegex, RegexOptions.IgnoreCase);
            foreach (var node in nodes)
            {
                if (string.IsNullOrWhiteSpace(node))
                    continue;
                var match = reg.Match(node.Trim());
                if (!match.Success)
                    continue;
                AddTrain(new Station(match.Groups["start"].Value), new Station(match.Groups["end"].Value),
                    ToInt(match.Groups["distance"].Value));
            }
        }


        public override string ToString()
        {
            var sb = new StringBuilder();
            foreach (var graph in _graphs)
            {
                var nexts = new List<string>();
                foreach (var next in graph.Nexts)
                {
                    nexts.Add(string.Format("{0}=> {1}", next.Key, next.Value.Distance));
                }
                sb.AppendLine(string.Format("{0}-> {{{1}}}", graph.Name, string.Join(",", nexts)));
            }
            return sb.ToString();
        }
    }
}
